﻿namespace Collaboris.Saf.Tests
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Reflection;

    using Definitions;

    using Engine;
    using Engine.Report;

    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Administration;

    using SharePoint.Wss.Webs;

    using Utils;

    public static class TestEnvironment
    {
        public static string WorkingDirectory = Path.Combine(Path.GetTempPath(), "TestAssistant");

        public static string WSSSite = "http://localhost:4411";

        public static string PublishingSite = "http://localhost:4422";

    }

    public class TestAssistant<TAction, TEntity> : IDisposable
        where TAction : class, IAction, new() where TEntity : class
    {
        #region Constants and Fields

        private TAction actionUnderTest;

        private IMacro macro;

        private string macroPath;

        private SafContext safContext;

        private SafClassDefinition classDefinition;

        private SPSite testSite;

        private SPWeb testWeb;

        private string testWebName;

        private SPWebApplication testWebApplication;

        private SPFarm testFarm;

        private List<string> supportingFiles = new List<string>();

        #endregion

        #region Constructors and Destructors

        public TestAssistant(string siteUrl, string newWebNameToCreate) : this (siteUrl, newWebNameToCreate, "STS#1")
        {
           
        }

        public TestAssistant(string siteUrl, string newWebNameToCreate, string newWebTemplate)
        {
            this.actionUnderTest = new TAction();

            this.testWebName = newWebNameToCreate;

            this.testSite = new SPSite(siteUrl);

            this.testWeb = this.CreateTestWeb(this.testWebName, newWebTemplate);

            this.testWebApplication = testSite.WebApplication;


        }

        public TestAssistant()
        {
            this.actionUnderTest = new TAction();
        }

        private SPWeb CreateTestWeb(string testWebName, string template)
        {
            using (SPWeb web = this.TestSite.RootWeb)
                return  SPWebHelper.CreateWeb(
                    web, true, testWebName, "test Web", template, testWebName, 1033, true, false);
        }

        private void DeleteTestWeb(string testWebName)
        {
            if (this.TestWeb != null)
            {
                //using (SPWeb web = this.TestSite.RootWeb)
                  //  SPWebHelper.DeleteWeb(web, true);

                SPWebHelper.DeleteWeb(TestWeb, true);
            }
        }

        #endregion

        #region Properties

        public SafContext Context
        {
            get
            {
                if (this.safContext == null)
                {
                    if (this.TestWeb != null)
                        this.safContext = SafContext.CreateContext(this.TestWeb, SPScope.Web);
                    else if ((this.TestSite != null))
                        this.safContext = SafContext.CreateContext(this.TestSite, SPScope.Site);
                    else if ((this.TestWebApplication != null))
                        this.safContext = SafContext.CreateContext(this.TestWebApplication, SPScope.WebApplication);
                    else if ((this.TestFarm != null))
                        this.safContext = SafContext.CreateContext(this.TestFarm, SPScope.Farm);
                    else
                    {
                        this.safContext = SafContext.CreateContext();
                    }
                }
                this.EnsureMandatoryContextProps(this.safContext);
                return this.safContext;
            }
            set
            {
                this.safContext = value;
            }
        }

        public IMacro Macro
        {
            get
            {
                if (this.macro == null)
                {
                    classDefinition = this.actionUnderTest.Definition;

                    string springXml = TargetXml;
                    
                    this.macroPath = Path.Combine(TestEnvironment.WorkingDirectory, Guid.NewGuid() + ".spring");
                    FileHelper.WriteFile(springXml, this.macroPath);

                    this.macro = MacroEngine.Create(this.macroPath, this.Context);
                }

                return this.macro;
            }
            set
            {
                this.macro = value;
            }
        }

        public SPFarm TestFarm
        {
            get
            {
                if (this.testFarm == null)
                    this.testFarm = SPFarm.Local;

                return this.testFarm;
            }
            set
            {
                this.testFarm = value;
            }
        }

        public SPWebApplication TestWebApplication
        {
            get
            {
                return this.testWebApplication;
            }
            set
            {
                this.testWebApplication = value;
            }
        }

        public SPSite TestSite
        {
            get
            {
                return this.testSite;
            }
            set
            {
                this.testSite = value;
            }
        }

        public SPWeb TestWeb
        {
            get
            {
                return this.testWeb;
            }
            set
            {
                this.testWeb = value;
            }
        }

        public TEntity FirstEntity
        {
            get
            {
                if (this.Macro != null)
                {
                    return this.Macro.Actions[0].EntityList[0] as TEntity;
                }

                return null;
            }
        }

        /// <summary>
        /// Gets the action wrapped inside of the fill Spring Xml.
        /// </summary>
        /// <remarks>
        /// This effectively used the SafClassAttributes and SafPropertyAttributes to do the job.
        /// </remarks>
        /// <returns></returns>
        private string TargetXml
        {
            get
            {
                if (classDefinition != null)
                    return classDefinition.ToSpring(false, false);

                return null;
            }
        }

        public TAction FirstAction
        {
            get
            {
                if (this.Macro != null)
                {
                    return this.Macro.Actions[0] as TAction;
                }

                return null;
            }
        }

        public string FirstSupportingFilePath
        {
            get
            {
                if (SupportingFiles != null && SupportingFiles.Count > 0)
                {
                    return SupportingFiles[0];
                }

                return null;
            }
        }

        public List<string> SupportingFiles
        {
            get
            {
                string binFolderPath = this.GetSupportedFilesLocation();
                foreach (string s in classDefinition.SupportFiles)
                {
                    if (!Path.IsPathRooted(s))
                    {
                        supportingFiles.Add(Path.Combine(binFolderPath, s));
                    }
                    else
                    {
                        supportingFiles.Add(s);
                    }
                }

                return supportingFiles;
            }
        }

        public virtual string GetSupportedFilesLocation()
        {
            return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            
        }

        #endregion

        #region Public Methods

        public MacroReport DoMacro()
        {
            MacroReport report = this.Macro.Do(this.safContext);

            return report;
        }

        public MacroReport UndoMacro()
        {
            MacroReport report = this.Macro.Undo(this.safContext);

            return report;
        }

        #endregion

        #region Implemented Interfaces

        #region IDisposable

        public void Dispose()
        {
            if (this.testWeb != null)
            {
                this.testWeb.Dispose();
            }

            if (this.testSite != null)
            {
                this.testSite.Dispose();
            }

            this.DeleteTestWeb(this.testWebName);

            FileHelper.DeleteFile(this.macroPath);
        }

        #endregion

        #endregion

        #region Helper Methods

        private void EnsureMandatoryContextProps(SafContext context)
        {
            context.ActionProperties.Add("CurrentDirectory", TestEnvironment.WorkingDirectory, true);
        }

        #endregion
    }
}